package com.example.blog.config;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.cache.NodeCache;
import org.apache.curator.framework.recipes.cache.NodeCacheListener;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DatasourceConfig {

    private CuratorFramework client = null;
    @Autowired
    private ApplicationContext context;


    @Bean(name = "mHikariPool")
    DataSource dataSource() throws Exception {
        init("127.0.0.1:2181");
        Map<String, String> config = getConfig();
        HikariDataSource hikariDataSource = buildDataSource(config);
        DynamicDatasource dynamicDatasource = new DynamicDatasource();
        dynamicDatasource.setHikariDataSource(hikariDataSource);
        return dynamicDatasource;
    }

    /**
     * 初始化 zk 连接，并注册监听器
     * @param zkUrl
     */
    public void init(String zkUrl) {
        ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3);
        client = CuratorFrameworkFactory.builder()
                .connectString(zkUrl)
                .retryPolicy(retryPolicy)
                .namespace("config-center")
                .build();
        client.start();
        registerListener();
    }

    /**
     * 从 zk 获取配置文件并组装成 map
     * @return
     * @throws Exception
     */
    public  Map<String,String> getConfig() throws Exception {
        if (client == null) {
            throw new RuntimeException("zk connection not init");
        }
        String data = new String(client.getData().forPath("/datasource"), "UTF-8");
        String[] split = data.split(";");
        Map<String,String> map = new HashMap<>();
        map.put("url", split[0]);
        map.put("username", split[1]);
        map.put("password", split[2]);
        return map;
    }

    /**
     * 构建连接池对象
     * @param properties
     * @return
     */
    public static HikariDataSource buildDataSource(Map<String, String> properties) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setPassword(properties.get("password"));
        hikariConfig.setUsername(properties.get("username"));
        hikariConfig.setJdbcUrl(properties.get("url"));
        hikariConfig.setDriverClassName("com.mysql.cj.jdbc.Driver");
        hikariConfig.setPoolName("mHikariPool");
        System.out.println("... build HikariCP.....");
        return new HikariDataSource(hikariConfig);
    }

    /**
     * 关闭连接池
     * @param dataSource
     */
    public void shutdown(HikariDataSource dataSource) {
        dataSource.close();
    }

    /**
     * 注册监听器
     */
    public void registerListener() {
        NodeCache nodeCache = new NodeCache(client, "/datasource");
        NodeCacheListener listener = () -> {
            Map<String, String> config = getConfig();
            HikariDataSource hikariDataSource = buildDataSource(config);
            DynamicDatasource dynamicDatasource = (DynamicDatasource)context.getBean("mHikariPool");
            HikariDataSource hikariDataSourceOld = dynamicDatasource.getHikariDataSource();
            dynamicDatasource.setHikariDataSource(hikariDataSource);
            shutdown(hikariDataSourceOld);

            /*
            无法直接通过 spring 容器，替换 datasource
             */
 /*
            DefaultSingletonBeanRegistry registry = (DefaultSingletonBeanRegistry)context.getAutowireCapableBeanFactory();
//            DefaultSingletonBeanRegistry registry = (DefaultListableBeanFactory)context.getAutowireCapableBeanFactory();
//            System.out.println("dep1  ..." + Arrays.toString(registry.getDependentBeans("mHikariPool")));
            HikariDataSource mHikariPoolOld = (HikariDataSource) registry.getSingleton("mHikariPool");
            if (mHikariPoolOld != null) {
                shutdown(mHikariPoolOld);
                registry.destroySingleton("mHikariPool");
                System.out.println("destroy datasource pool");
            }
            registry.registerSingleton("mHikariPool", hikariDataSource);
//            ((AbstractApplicationContext)context).refresh(); can only refresh once

*/
        };
        nodeCache.getListenable().addListener(listener);
        try {
            // 这里要设置成 true,防止一启动就触发事件
            nodeCache.start(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
